/*
 * Copyright Thoughtworks, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.thoughtworks.go.spark.spa

import com.thoughtworks.go.config.CruiseConfig
import com.thoughtworks.go.config.validation.GoConfigValidity
import com.thoughtworks.go.server.service.GoConfigService
import com.thoughtworks.go.spark.spring.SPAAuthenticationHelper
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.mockito.Mock
import spark.ModelAndView
import spark.Request
import spark.Response
import spark.TemplateEngine

import static org.mockito.Mockito.when
import static org.mockito.MockitoAnnotations.openMocks
import static org.assertj.core.api.Assertions.assertThat

class AdminConfigXmlControllerTest {

    @Mock
    private SPAAuthenticationHelper authenticationHelper
    @Mock
    private TemplateEngine engine
    @Mock
    private GoConfigService goConfigService
    @Mock
    private CruiseConfig cruiseConfig
    @Mock
    private GoConfigValidity configValidity

    private AdminConfigXmlController controller

    @BeforeEach
    void setUp() {
        openMocks(this)
        controller = new AdminConfigXmlController(authenticationHelper, engine, goConfigService)
    }

    @Test
    void 'should return correct controller base path'() {
        assertThat(controller.controllerBasePath()).isEqualTo("/spark/admin/config_xml")
    }

    @Test
    void 'should return show view with config data'() {
        // Given
        Request request = mock(Request.class)
        Response response = mock(Response.class)

        when(goConfigService.getCurrentConfig()).thenReturn(cruiseConfig)
        when(goConfigService.checkConfigFileValid()).thenReturn(configValidity)
        when(goConfigService.xml()).thenReturn("<cruise>test config</cruise>")
        when(cruiseConfig.getMd5()).thenReturn("test-md5")
        when(goConfigService.fileLocation()).thenReturn("/path/to/config.xml")
        when(configValidity.isValid()).thenReturn(true)

        // When
        ModelAndView result = controller.show(request, response)

        // Then
        assertThat(result).isNotNull()
        assertThat(result.getViewName()).isEqualTo("admin/config_xml/show")
        assertThat(result.getModel()).containsKey("configContent")
        assertThat(result.getModel()).containsKey("configMd5")
        assertThat(result.getModel()).containsKey("configLocation")
        assertThat(result.getModel()).containsKey("configValid")
        assertThat(result.getModel()).containsKey("pageName")
        assertThat(result.getModel().get("configContent")).isEqualTo("<cruise>test config</cruise>")
        assertThat(result.getModel().get("configMd5")).isEqualTo("test-md5")
        assertThat(result.getModel().get("configLocation")).isEqualTo("/path/to/config.xml")
        assertThat(result.getModel().get("configValid")).isEqualTo(true)
        assertThat(result.getModel().get("pageName")).isEqualTo("admin-config-xml")
    }

    @Test
    void 'should return edit view with config data'() {
        // Given
        Request request = mock(Request.class)
        Response response = mock(Response.class)

        when(goConfigService.getCurrentConfig()).thenReturn(cruiseConfig)
        when(goConfigService.checkConfigFileValid()).thenReturn(configValidity)
        when(goConfigService.xml()).thenReturn("<cruise>test config</cruise>")
        when(cruiseConfig.getMd5()).thenReturn("test-md5")
        when(goConfigService.fileLocation()).thenReturn("/path/to/config.xml")
        when(configValidity.isValid()).thenReturn(true)

        // When
        ModelAndView result = controller.edit(request, response)

        // Then
        assertThat(result).isNotNull()
        assertThat(result.getViewName()).isEqualTo("admin/config_xml/edit")
        assertThat(result.getModel()).containsKey("configContent")
        assertThat(result.getModel()).containsKey("configMd5")
        assertThat(result.getModel()).containsKey("configLocation")
        assertThat(result.getModel()).containsKey("configValid")
        assertThat(result.getModel()).containsKey("pageName")
        assertThat(result.getModel().get("pageName")).isEqualTo("admin-config-xml-edit")
    }

    @Test
    void 'should handle errors gracefully in show method'() {
        // Given
        Request request = mock(Request.class)
        Response response = mock(Response.class)

        when(goConfigService.getCurrentConfig()).thenThrow(new RuntimeException("Config loading failed"))

        // When
        ModelAndView result = controller.show(request, response)

        // Then
        assertThat(result).isNotNull()
        assertThat(result.getViewName()).isEqualTo("admin/config_xml/error")
        assertThat(result.getModel()).containsKey("error")
        assertThat(result.getModel().get("error")).isEqualTo("Failed to load configuration: Config loading failed")
    }

    @Test
    void 'should handle errors gracefully in edit method'() {
        // Given
        Request request = mock(Request.class)
        Response response = mock(Response.class)

        when(goConfigService.getCurrentConfig()).thenThrow(new RuntimeException("Config loading failed"))

        // When
        ModelAndView result = controller.edit(request, response)

        // Then
        assertThat(result).isNotNull()
        assertThat(result.getViewName()).isEqualTo("admin/config_xml/error")
        assertThat(result.getModel()).containsKey("error")
        assertThat(result.getModel().get("error")).isEqualTo("Failed to load configuration for editing: Config loading failed")
    }

    private static <T> T mock(Class<T> clazz) {
        return org.mockito.Mockito.mock(clazz)
    }
}
